package ms.unicorn.reasoning.views;



import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import ms.unicorn.Element;
import ms.unicorn.dsl.analysis.AnalysisRegistryService.AnalysisRegistryListener;
import ms.unicorn.dsl.analysis.IAnalysis;
import ms.unicorn.dsl.analysis.UnicornService;
import ms.unicorn.reasoning.AnnotatedAlternative;
import ms.unicorn.reasoning.DAT;
import ms.unicorn.reasoning.FinalAnnotatedAlternative;
import ms.unicorn.reasoning.MaxBeliefAnalysis;

import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.ui.part.*;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.gef.EditPart;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.jface.viewers.*;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.jface.action.*;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.ui.*;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.SWT;

import swing2swt.layout.BorderLayout;


/**
 * This sample class demonstrates how to plug-in a new
 * workbench view. The view shows data obtained from the
 * model. The sample creates a dummy model on the fly,
 * but a real implementation would connect to the model
 * available either in this or another plug-in (e.g. the workspace).
 * The view is connected to the model using a content provider.
 * <p>
 * The view uses a label provider to define how model
 * objects should be presented in the view. Each
 * view can present the same model objects using
 * different labels and icons, if needed. Alternatively,
 * a single label provider can be shared between views
 * in order to ensure that objects of the same type are
 * presented in the same way everywhere.
 * <p>
 */

public class AnalysisResultView extends ViewPart implements ISelectionListener, AnalysisRegistryListener {

	/**
	 * The ID of the view as specified by the extension.
	 */
	public static final String ID = "ms.unicorn.reasoning.views.AnalysisResultView";

	private TableViewer tblDA;
	private Action action1;
	private Action action2;
	private Action doubleClickAction;
	
	private List<IAnalysis> analyses;

	private Combo analysisCombo;

	private Element selectedElement;

	private TableViewer tblDAT;

	private CLabel lblDAT;
	
	class ViewContentProvider implements IStructuredContentProvider {
		
		public void inputChanged(Viewer v, Object oldInput, Object newInput) {
		}
		public void dispose() {
		}
		public Object[] getElements(Object parent) {
			if (parent instanceof DAT)
				return ((DAT) parent).toArray();
			return null;
				
		}
	}
	
	class ViewLabelProvider extends LabelProvider implements ITableLabelProvider {
		
		public String getColumnText(Object obj, int index) {
			AnnotatedAlternative alt = (AnnotatedAlternative) obj;
			switch (index) {
			case 0: return alt.toString();
			case 1: return String.format("%.2f",alt.getMaxBelief());
			case 2: return String.format("%.2f",alt.getDeferalBelief());
			case 3: if (alt instanceof FinalAnnotatedAlternative)
						return  String.format("%.2f", ((FinalAnnotatedAlternative) alt).getMaxDisbelief());
					return alt.getEvolutionPath();
			}
			return getText(obj);
		}
		
		public Image getColumnImage(Object obj, int index) {
			switch(index) {
			case 0: return getImage(obj);
			case 1: return null;
			case 2: return null;
			case 3: return null;
			}
			return getImage(obj);
		}
		
		public Image getImage(Object obj) {
			return PlatformUI.getWorkbench().
					getSharedImages().getImage(ISharedImages.IMG_OBJ_ELEMENT);
		}
	}
	
	class NameSorter extends ViewerSorter {
	}

	/**
	 * The constructor.
	 */
	public AnalysisResultView() {
		this.analyses = (List<IAnalysis>) UnicornService.getAnalysisRegistryService().getAnalyses(MaxBeliefAnalysis.class);
		UnicornService.getAnalysisRegistryService().registerListener(this);
	}

	public MaxBeliefAnalysis getSelectedAnalysis() {
		return (MaxBeliefAnalysis) (analysisCombo.getSelectionIndex() >= 0 ? analyses.get(analysisCombo.getSelectionIndex()) : null);
	}
	
	
	@Override
	public void dispose() {
		UnicornService.getAnalysisRegistryService().unregisterListener(this);
		ISelectionService service = (ISelectionService) getSite().getService(ISelectionService.class);
		service.removeSelectionListener(this);
		super.dispose();
	}
	/**
	 * This is a callback that will allow us
	 * to create the viewer and initialize it.
	 */
	public void createPartControl(Composite parent) {
		Composite container = new Composite(parent, SWT.NONE);
		container.setLayout(new BorderLayout(0, 0));
		
		// create NORTH label
		final Composite composite = new Composite(container, SWT.BORDER);
		composite.setLayoutData(BorderLayout.NORTH);
		composite.setLayout(new BorderLayout(0, 0));
		
		this.analysisCombo = new Combo(composite, SWT.DROP_DOWN|SWT.READ_ONLY);
		analysisCombo.setLayoutData(BorderLayout.NORTH);
						
		// create CENTER tab folder
		CTabFolder folder = new CTabFolder(container, SWT.NONE);
		folder.setTabPosition(SWT.BOTTOM);
		folder.setLayoutData(BorderLayout.CENTER);
		
		// create tab Alternatives
		CTabItem detailTab = new CTabItem(folder,SWT.NONE);
		detailTab.setText("Design Alternatives");		
		
		tblDA = new TableViewer(folder, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
		
		
		Table table = tblDA.getTable();
		table.setHeaderVisible(true);
		
		TableColumn column = new TableColumn(table, SWT.LEFT);
		column.setText("Alternative");
		column.setWidth(150);
		
		column = new TableColumn(table, SWT.RIGHT);
		column.setText("Max Belief");
		column.setWidth(80);
		
		column = new TableColumn(table, SWT.RIGHT);
		column.setText("Residual DisBelief");
		column.setWidth(100);
		
		column = new TableColumn(table, SWT.RIGHT);
		column.setText("Max DisBelief");
		column.setWidth(100);
		
		detailTab.setControl(table);
		tblDA.setContentProvider(new ViewContentProvider());
		tblDA.setLabelProvider(new ViewLabelProvider());
		tblDA.setSorter(null);
		
		// create DAT viewer tab
		CTabItem datTab = new CTabItem(folder, SWT.BORDER);
		datTab.setText("DAT Viewer");
		Composite datContainer = new Composite(folder, SWT.NONE);
		datContainer.setLayout(new BorderLayout(0, 0));
		
		
		lblDAT = new CLabel(datContainer, SWT.NONE);
		lblDAT.setLayoutData(BorderLayout.NORTH);
		lblDAT.setText("[No node selected or analysis is not initialized]");
		
		tblDAT = new TableViewer(datContainer, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
		
		table = tblDAT.getTable();
		tblDAT.setContentProvider(new ViewContentProvider());
		tblDAT.setLabelProvider(new ViewLabelProvider());
		tblDAT.setSorter(null);
		
		table.setLayoutData(BorderLayout.CENTER);
		datTab.setControl(datContainer);
		
		table.setHeaderVisible(true);
		
		column = new TableColumn(table, SWT.LEFT);
		column.setText("Alternative");
		column.setWidth(150);
		
		column = new TableColumn(table, SWT.RIGHT);
		column.setText("Max Belief");
		column.setWidth(80);
		
		column = new TableColumn(table, SWT.RIGHT);
		column.setText("Residual DisBelief");
		column.setWidth(100);
		
		column = new TableColumn(table, SWT.RIGHT);
		column.setText("Evolution Path");
		column.setWidth(100);
		
		folder.setSelection(detailTab);
		
		// Create the help context id for the viewer's control
		makeActions();
		hookContextMenu();
		hookDoubleClickAction();
		contributeToActionBars();
		
		analysisCombo.addModifyListener(new ModifyListener() {
			
			@Override
			public void modifyText(ModifyEvent e) {
				tblDA.setInput(getSelectedAnalysis().getAlternatives());
			}
		});
		
		for (IAnalysis analysis: analyses)
			analysisCombo.add(analysis.getText());
		
		ISelectionService service = (ISelectionService) getSite().getService(ISelectionService.class);
		service.addSelectionListener(this);
	}

	private void hookContextMenu() {
		MenuManager menuMgr = new MenuManager("#PopupMenu");
		menuMgr.setRemoveAllWhenShown(true);
		menuMgr.addMenuListener(new IMenuListener() {
			public void menuAboutToShow(IMenuManager manager) {
				AnalysisResultView.this.fillContextMenu(manager);
			}
		});
		Menu menu = menuMgr.createContextMenu(tblDA.getControl());
		tblDA.getControl().setMenu(menu);
		getSite().registerContextMenu(menuMgr, tblDA);
	}

	private void contributeToActionBars() {
		IActionBars bars = getViewSite().getActionBars();
		fillLocalPullDown(bars.getMenuManager());
		fillLocalToolBar(bars.getToolBarManager());
	}

	private void fillLocalPullDown(IMenuManager manager) {
		manager.add(action1);
		manager.add(new Separator());
		manager.add(action2);
	}

	private void fillContextMenu(IMenuManager manager) {
		manager.add(action1);
		manager.add(action2);
		// Other plug-ins can contribute there actions here
		manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
	}
	
	private void fillLocalToolBar(IToolBarManager manager) {
		manager.add(action1);
		manager.add(action2);
	}

	private void makeActions() {
		action1 = new Action() {
			public void run() {
				showMessage("Action 1 executed");
			}
		};
		action1.setText("Action 1");
		action1.setToolTipText("Action 1 tooltip");
		action1.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().
			getImageDescriptor(ISharedImages.IMG_OBJS_INFO_TSK));
		
		action2 = new Action() {
			public void run() {
				showMessage("Action 2 executed");
			}
		};
		action2.setText("Action 2");
		action2.setToolTipText("Action 2 tooltip");
		action2.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().
				getImageDescriptor(ISharedImages.IMG_OBJS_INFO_TSK));
		doubleClickAction = new Action() {
			public void run() {
				ISelection selection = tblDA.getSelection();
				Object obj = ((IStructuredSelection)selection).getFirstElement();
				showMessage("Double-click detected on "+obj.toString());
			}
		};
	}

	private void hookDoubleClickAction() {
		tblDA.addDoubleClickListener(new IDoubleClickListener() {
			public void doubleClick(DoubleClickEvent event) {
				doubleClickAction.run();
			}
		});
	}
	private void showMessage(String message) {
		MessageDialog.openInformation(
			tblDA.getControl().getShell(),
			"Evolution Analysis",
			message);
	}

	/**
	 * Passing the focus request to the viewer's control.
	 */
	public void setFocus() {
		tblDA.getControl().setFocus();
	}
	
	public void setSelectedAnalysis(IAnalysis analysis) {
		int idx = analyses.indexOf(analysis);
		if (idx >= 0) {
			analysisCombo.select(idx);
		}
	}

	@Override
	public void selectionChanged(IWorkbenchPart part, ISelection selection) {
		this.selectedElement = null;
		if (selection instanceof StructuredSelection) {
			Object object = ((StructuredSelection) selection).getFirstElement();
			if (object instanceof EditPart) {
				Object model = ((EditPart) object).getModel();
				if (model instanceof View) {
					EObject element = ((View) model).getElement();
					if (element instanceof Element)
						//this.selectedElement = (Element) element;
						setSelectedElement((Element) element);
				}
			}
			else if (object instanceof Element)
				//this.selectedElement = (Element) object;
				setSelectedElement((Element) object);
		}
	}
	
	protected void setSelectedElement(Element sel) {
		if (sel != selectedElement) {
			selectedElement = sel;
			if (selectedElement != null) {
				lblDAT.setText(String.format("DAT of node '%s'", selectedElement.getName() != null ? selectedElement.getName() : selectedElement.getId()));
				DAT dat = null;
				if (getSelectedAnalysis() != null) {
					dat = getSelectedAnalysis().getDAT(selectedElement);
				}
				tblDAT.setInput(dat);
			}
		}
	}

	@Override
	public void analysisChanged(int what, IAnalysis changed) {
		switch(what) {
		case ANALYSIS_CLEAR:
			analyses.clear();
			analysisCombo.removeAll();
			break;
		case ANALYSIS_REGISTERED:
			if (changed instanceof MaxBeliefAnalysis) {
				analyses.add(changed);
				analysisCombo.add(changed.toString());
				if (analysisCombo.getSelectionIndex() < 0)
					analysisCombo.select(0);
			}
			break;
		case ANALYSIS_UNREGISTERED:
			analyses.remove(changed);
			analysisCombo.remove(changed.toString());
			break;
		case ANALYSIS_CHANGED:
			if (changed == getSelectedAnalysis())
				tblDA.refresh();
			break;
		case ANALYSIS_ACTIVATED:
			if (changed != getSelectedAnalysis())
				setSelectedAnalysis(changed);
		}			
	}
}